/**
 * Copyright 2007 Igor Chaves Cananéa
 *
 * This file is part of PBSFW
 *
 * PBSFW is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * PBSFW is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef _PROPERTY_H_
#define _PROPERTY_H_

#include <set>
#include <map>
#include <string>
#include "resetable.h"
using namespace std;

namespace pbsfw {
	/**
	* A property reflects some state of the simulation and can be recordered
	* by a reporter. Only Actors, through the getProperties() method which is
	* called by the simulator, can register properties. This does not prevent
	* other entities from the simulation to have properties, but they MUST be
	* available through an actor.
	*/
	class Property {
		private:
			const char *name;
			const char *owner;
			const char *secondName;

		protected:
			/**
			* Property constructer.
			*
			* @param name The name of the property.
			* @param owner The actor that owns the property.
			* @param secondName A secondary name that can be used for
			* differentiation.
			*/
			Property(const char *name, const char *owner,
					const char *secondName)
				: name(name), owner(owner), secondName(secondName) {}

			Property(const Property &p) {
				(*this) = p;
			}

			const Property &operator=(const Property &p) {
				this->name = p.name;
				this->owner = p.owner;
				this->secondName = p.secondName;
				return *this;
			}

		public:
			virtual ~Property() {}

			const char *getName() const {
				return this->name;
			}

			const char *getOwner() const {
				return this->owner;
			}

			const char *getSecondName() const {
				return this->secondName;
			}


			/**
			* Must return the current value of the property as a double. It is
			* called by the reporters.
			*/
			virtual double getValue() const = 0;
	};

	typedef set<Property*> PropertyTable;
	typedef set<Property*>::iterator PropertyIterator;
	typedef multimap<string, Property*> PropertyNameTable;
	typedef multimap<string, Property*>::iterator PropertyNameIterator;
	typedef Property* PropertyItem;

	/**
	* A ReferenceValueProperty has a direct reference to a simulation state
	* variable, which must necessarily be a double. This ensures that if they
	* value is updated by the simulation, getValue() will always return the
	* most current value.
	*
	* Since the value is referenced, it is assumed that the actor that
	* registered the property will reset it.
	*/
	class ReferencedValueProperty : public Property {
		private:
			double &value;

		public:
			/**
			* ReferenceValueProperty constructor.
			*
			* @param name The name of the property.
			* @param owner The owner of the property.
			* @param value A refernce to the value that will be reported.
			* @param secondName A secondary name that can be used for
			* differentiation.
			*/
			ReferencedValueProperty(const char *name, const char *owner,
									double &value, const char *secondName = "*")
				: Property(name, owner, secondName), value(value) {}

			double getValue() const {
				return this->value;
			}
	};

	/**
	* A SingleValueProperty must be manually updated whenever need, in order to
	* store the current value and it is done by calling the update() method.
	* The initial value of the property MUST be passed to the constructor and
	* at each reset, it will set the current value to it.
	*
	* This property is usefull if the value that must be reported must be
	* computed at every update.
	*/
	class SingleValueProperty : public Property, public Resetable {
		private:
			double startValue;
			double value;

		public:
			/**
			* The SingleValueProperty constructor
			*
			* @param name The name of the property.
			* @param owner The owner of the property.
			* @param value The initial value of the property
			* @param secondName A secondary name that can be used for
			* differentiation.
			*/
			SingleValueProperty(const char *name, const char *owner,
								double value, const char *secondName = "*")
				: Property(name, owner, secondName), startValue(value),
				value(value) {}

			/**
			* Updates the current value
			*
			* @param value The new value of the property.
			*/
			void updateValue(double value) {
				this->value = value;
			}

			void incrementValue() {
				this->value += 1.0;
			}

			double getValue() const {
				return this->value;
			}

			const double &getValueReference() const {
				return this->value;
			}

			void reset() {
				this->value = this->startValue;
			}
	};

	class ComplexProperty : public Property {
		public:
			ComplexProperty(const char *name, const char *owner,
							const char *secondName = "*")
				: Property(name, owner, secondName) {
			}

			virtual void addProperty(const Property *property) = 0;
	};
};

#endif
